<script setup lang="ts">
import { watchOnce } from '@vueuse/core'
import type { ContextMenuData, ContextMenuProps } from './type'

defineOptions({
  inheritAttrs: false,
})

// import './style.css';
// ------------------use------------------
// #region
const props = withDefaults(
  defineProps<ContextMenuProps>(),
  {
    //   position: () => ({
    //     x: 0,
    //     y: 0
    //   }),
    left: 0,
    right: 0,
    target: 'body',
    data: () => [
      {
        label: '首页',
        class: 'text-red',
        divider: true,
        onClick() {
          console.log('我被点击了')
          return true
        },
      },
      {
        label: '其它',
        class: 'text-blue',
      },
    ],
  },
)

const emits = defineEmits<{
  menuClick: [row: Partial<ContextMenuData>]
  hide: []
}>()
const isShow = defineModel<boolean>({
  default: false,
})
const [isMounted, setIsMounted] = useState(false)
const [size, setSize] = useState({
  width: 0,
  height: 0,
})
// const [isShow, setIsShow] = useState<boolean>(false);
// #endregion
// ------------------静态变量------------------
// #region
// #endregion
// ------------------动态变量------------------
// #region

// #endregion
// ------------------ref-----------
// #region
const rightMenuRef = ref<HTMLElement>()
// #endregion
// ------------------reactive------
// #region

// #endregion
// ------------------computed------
// #region
const newPosition = computed(() => {
  let x = props.position?.x ?? props.left ?? 0
  let y = props.position?.y ?? props.top ?? 0
  if (!rightMenuRef.value) {
    return { x, y }
  }
  const { width, height } = unref(size)

  // 计算边界问题
  // y
  if (y + height + 10 > window.innerHeight) {
    y = y - height
  }
  // x
  if (x + width + 10 > window.innerWidth) {
    x = x - width
  }

  return { x, y }
})
// #endregion
// ------------------生命周期------------------
// #region
onMounted(async () => {
  bindEvent()
  setIsMounted(true)
  await nextTick()
  getSize()
})
// #endregion
// ------------------内置方法---------------------
// #region

// #endregion
// ------------------方法---------------------
// #region
/** 获取菜单大小 */
async function getSize() {
  const container = unref(rightMenuRef)!
  container.style.setProperty('--x', '9999px')
  container.style.setProperty('--y', '9999px')
  isShow.value = true
  await nextTick()
  const { width, height }
    = rightMenuRef.value!.getBoundingClientRect()
  setSize({ width, height })
  isShow.value = false
  await nextTick()

  // ！避免闪动
  watchOnce(
    () => isShow.value,
    () => {
      container.style.removeProperty('--x')
      container.style.removeProperty('--y')
    },
  )
}
function bindEvent() {
  // ? 点击非菜单区域隐藏
  window.addEventListener('mousedown', handleHide)
}

function handleHide(e: Event) {
  //   console.log(123, e);

  if (!isShow.value || !rightMenuRef.value || !e.target)
    return
  const isEqual1 = rightMenuRef.value.isEqualNode(
    e.target as Node,
  )
  const isEqual = (
    e.target as HTMLElement
  )?.className?.includes?.('context-menu')
  if (isEqual || isEqual1)
    return
  isShow.value = false
  emits('hide')
}
async function withClick(onClick?: ContextMenuData['onClick'], row?: Partial<ContextMenuData>, e?: Event) {
  if (row?.disabled)
    return
  emits('menuClick', row!)
  if (!onClick || !row || !e) {
    isShow.value = false
    emits('hide')
    return
  }
  const isBol = (await onClick)(row, e)
  if (typeof isBol === 'boolean') {
    if (isBol) {
      isShow.value = false
      emits('hide')
    }
  }
  else {
    isShow.value = false
    emits('hide')
  }
}
// #endregion
defineExpose({
  getSize,
})
</script>

<template>
  <Teleport v-if="isMounted" :to="target">
    <Transition
      leave-active-class="animate__animated context-menu-animate animate__faster  animate__fadeOut"
      enter-active-class="animate__animated context-menu-animate animate__faster  animate__fadeIn"
    >
      <div
        v-show="isShow"
        ref="rightMenuRef"
        class="flex-direction context-menu el-popper el-popover flex"
        v-bind="$attrs"
        @contextmenu.prevent.stop
      >
        <slot>
          <template v-for="(item, i) in data" :key="i">
            <div
              class="context-menu-item text-hidden-one"
              :class="[
                item.type,
                item.class,
                {
                  disabled: item.disabled,
                },
              ]"
              @click="withClick(item.onClick, item, $event)"
            >
              {{ item.label }}
            </div>
            <el-divider
              v-if="item.divider && i !== data.length - 1"
              class="margin-tb-0"
            />
          </template>
        </slot>
      </div>
    </Transition>
  </Teleport>
</template>

<style lang="scss">
[class].context-menu {
  $MenuType: (
    danger,
    success,
    warning,
    info,
    primary,
    danger
  );

  --x: v-bind(`${newPosition.x}px`);
  --y: v-bind(`${newPosition.y}px`);
  position: absolute;
  top: 0px;
  left: 0px;
  z-index: 9999;
  transform: translate(var(--x), var(--y));
  padding: 6px !important;

  &-animate {
    --animate-duration: 0.3s;
  }
  &-item {
    cursor: pointer;
    box-sizing: border-box;
    font-size: 12px;
    transition: all 0.2s;
    padding: 5px 12px;
    border-radius: var(--el-border-radius-base);
  }
  &-item.disabled {
    cursor: initial;
    color: var(--el-text-color-disabled);
  }
  &-item:not(.disabled):hover {
    background-color: var(--el-color-primary-light-9);
    color: var(--el-color-primary);
  }
  @each $type in $MenuType {
    .context-menu-item:not(.disabled).#{$type} {
      color: var(--el-color-#{$type});
      &:hover {
        background-color: var(--el-color-#{$type}-light-9);
      }
    }
  }

  .el-divider {
    margin: 4px 0px 3px;
  }
}
//------------------组件样式------------------
//-------------样式穿透-------------
</style>
